Passport.js 是一個使用者登入跟認證系統. 它有提供很多套件(strategy)給很多不同第三方認證 像facebook, google. 這個教學一樣會分成兩段, 第一段model篇, 第二段routes & view 篇
以下的gif 就是我們要做的功能.
// package.json
...
"dependencies": {
"bcryptjs": "^2.4.3",
"body-parser": "~1.17.1",
"connect-flash": "^0.1.1",
"cookie-parser": "~1.4.3",
"debug": "~2.6.3",
"ejs": "~2.5.6",
"express": "~4.15.2",
"express-messages": "^1.0.1",
"express-session": "^1.15.3",
"express-validator": "^3.2.0",
"mongoose": "^4.10.6",
"morgan": "~1.8.1",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"serve-favicon": "~2.4.2"
}
// model/users.js
//先載入我們要的library
var mongoose = require('mongoose')
var bcrypt = require('bcryptjs')
//創造資料庫需要的欄位(schema)
var UserSchema = mongoose.Schema({
username: { type: String, index: true},
password: { type: String}
})
var User = module.exports = mongoose.model('User', UserSchema)
// 建立createUser方法, 然後用bcrypt加密 + 存檔
module.exports.createUser = function(newUser, callback) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash){
newUser.password = hash
newUser.save(callback)
})
})
}
// getUserByUsername, 用username來找使用者
module.exports.getUserByUsername = function(username, callback) {
var query = { username: username }
User.findOne(query, callback)
}
// getUserById, 用id來找使用者
module.exports.getUserById = function(id, callback) {
User.findById(id, callback)
}
// comparePassword, 當使用者登入的時候我們要比對登入密碼跟我們資料庫密碼相同
module.exports.comparePassword = function(candidatePassword, hash,callback) {
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err
callback(null, isMatch)
});
}
var passport = require('passport')
var LocalStrategy = require('passport-local')
var mongoose = require('mongoose')
var flash = require('connect-flash');
var users = require('./routes/users');
...
mongoose.connect('mongodb://localhost/loginappv2');
var db = mongoose.connection;
...
app.use(passport.initialize());
app.use(passport.session());
// GET 登入頁面
router.get('/signin', function(req, res, next) {
console.log(res.locals)
res.render('signin');
});
// POST 登入頁面
router.post('/signin',
passport.authenticate('local', {
successRedirect: '/users/profile',
failureRedirect: '/users/signin',
failureFlash: true
}),
function(req, res) {
res.redirect('/users/profile')
});
// GET 註冊頁面
router.get('/signup', function(req, res, next) {
res.render('signup', {errors: ''});
});
// POST 註冊頁面
router.post('/signup', function(req, res, next) {
// Parse Info
var username = req.body.username
var password = req.body.password
//Create User
var newUser = new User({
username: username,
password: password
})
User.createUser(newUser, function(err, user){
if(err) throw err;
})
res.redirect('/users/signin')
});
// GET 登入後的 profile 頁面
// 這邊有用ensureAuthenticated 來看使用者是不是已登入過, 如果沒有就不可以來這一頁
router.get('/profile', ensureAuthenticated, function(req, res, next) {
console.log(req.user)
res.render('profile', {
user: req.user.username
});
});
// GET 登出
router.get('/logout', function(req, res, next) {
req.logout()
req.flash('success_msg', 'You are logged out')
res.redirect('/users/signin')
})
module.exports = router;
function ensureAuthenticated(req, res, next){
if(req.isAuthenticated()){
return next();
} else {
req.flash('error_msg', 'you are not logged in')
res.redirect('/users/signin')
}
}
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err
if(isMatch) {
return done(null, user)
} else {
return done(null, false, {message: 'Invalid password'})
}
})
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});